#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
#include <functional>
#include <memory>
#include "InetAddr.hpp"
#include "Log.hpp"

// 模板方法模式
namespace socket_ns
{
  class Socket;
  const static int gbacklog = 8;
  using socket_sptr = std::shared_ptr<Socket>;
  enum
  {
    SOCKET_ERROR = 1,
    BIND_ERROR,
    LISTEN_ERROR,
    USAGE_ERROR
  };
  class Socket
  {
  public:
    virtual void CreateSocketOrDie() = 0;
    virtual void BindSocketOrDie(InetAddr &addr) = 0;
    virtual void ListenSocketOrDie() = 0;
    virtual int Accepter(InetAddr *addr) = 0;
    virtual bool Connector(InetAddr &addr) = 0;
    virtual void SetSocketAddrReuse() = 0;
    virtual int SockFd() = 0;
    virtual int Recv(std::string *out) = 0;
    virtual int Send(const std::string &in) = 0;
    // virtual void Other()=0;
  public:
    void BuildListenSocket(InetAddr &addr)
    {
      CreateSocketOrDie();
      SetSocketAddrReuse();
      BindSocketOrDie(addr);
      ListenSocketOrDie();
    }
    bool BuildClientSocket(InetAddr &addr)
    {
      CreateSocketOrDie();
      return Connector(addr);
    }
    // void BuildUdpSocket()
    // {
    //   CreateSocketOrDie();
    //   BindSocketOrDie();
    // }
  };

  class TcpSocket : public Socket
  {
  public:
    TcpSocket(int fd = -1) : _sockfd(fd)
    {
    }
    void CreateSocketOrDie() override
    {
      // 1. 创建流式套接字
      _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
      if (_sockfd < 0)
      {
        LOG(FATAL, "socket error");
        exit(SOCKET_ERROR);
      }
      LOG(DEBUG, "socket create success,sockfd is : %d", _sockfd);
    }
    void BindSocketOrDie(InetAddr &addr) override
    {
      // 2. 绑定
      struct sockaddr_in local;            // struct sockaddr_in 系统提供的数据类型。local是变量，用户栈上开辟空间。
      bzero(&local, sizeof(local));        // 将从&local开始的sizeof(local)大小的内存区域置零
      local.sin_family = AF_INET;          // 设置网络通信方式
      local.sin_port = htons(addr.Port()); // port要经过网络传输给对面，所有需要从主机序列转换为网络序列
      local.sin_addr.s_addr = inet_addr(addr.Ip().c_str());

      int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
      if (n < 0)
      {
        LOG(FATAL, "bind error");
        exit(BIND_ERROR);
      }
      LOG(DEBUG, "bind success,sockfd is : %d", _sockfd);
    }
    void ListenSocketOrDie() override
    {
      // 3. tcp是面向连接的，所以通信之前，必须先建立连接，服务器是被链接的
      //  tcpserver启动，未来首先要一直等待客户端的连接，listen
      int n = listen(_sockfd, gbacklog);
      if (n < 0)
      {
        LOG(FATAL, "listen error");
        exit(LISTEN_ERROR);
      }
      LOG(DEBUG, "listen success,sockfd is : %d", _sockfd);
    }
    int Accepter(InetAddr *addr) override
    {
      struct sockaddr_in peer;
      socklen_t len = sizeof(peer);
      // accept会阻塞等待，直到有客户端连接
      int sockfd = ::accept(_sockfd, (struct sockaddr *)&peer, &len);
      if (sockfd < 0)
      {
        LOG(WARNING, "accept error");
        return -1;
      };
      *addr = peer;
      // socket_sptr sock = std::make_shared<TcpSocket>(sockfd);
      return sockfd;
    }
    bool Connector(InetAddr &addr) override
    {
      // 构建目标主机的socket信息
      struct sockaddr_in server;
      memset(&server, 0, sizeof(server)); // bzero
      server.sin_family = AF_INET;
      server.sin_port = htons(addr.Port());
      server.sin_addr.s_addr = inet_addr(addr.Ip().c_str());

      int n = connect(_sockfd, (struct sockaddr *)&server, sizeof(server));
      if (n < 0)
      {
        std::cerr << "connect error" << std::endl;
        return false;
      }
      return true;
    }
    void SetSocketAddrReuse() override
    {
      int opt = 1;
      ::setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
    }
    int Recv(std::string *out)
    {
      char inbuffer[1024];
      ssize_t n = ::recv(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0);
      if (n > 0)
      {
        inbuffer[n] = 0;
        *out += inbuffer; // 为什么是+=
      }
      return n;
    }
    int Send(const std::string &in)
    {
      int n = ::send(_sockfd, in.c_str(), in.size(), 0);
      return n;
    }
    int SockFd() override
    {
      return _sockfd;
    }

  private:
    int _sockfd;
  };
}